package com.example.extension.framework;

import java.util.Map;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;

/**
 * 
 * @author jie.deng
 */
@Component
public class ExtensionRegister {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExtensionRegister.class);

    // R=扩展点编号 C=适用场景编号 V=扩展点实现bean
    private static volatile Table<String, String, Object> extImplTable = HashBasedTable.create();

    @Autowired
    private ApplicationContext applicationContext;

    public synchronized void registrationComponent() {
        Map<String, Object> map = applicationContext.getBeansWithAnnotation(ExtensionImpl.class);
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Object bean = entry.getValue();
            doRegistrationComponent(bean);
        }

        for (Cell<String, String, Object> cell : extImplTable.cellSet()) {
            LOGGER.warn("注册的扩展实现类{extPointCode={},scenarioCode={},extImpl={}}", cell.getRowKey(), cell.getColumnKey(),
                cell.getValue().getClass().getName());
        }
    }

    private void doRegistrationComponent(Object bean) {
        // 获取bean上的注解
        Object orgBean = bean;
        while (AopUtils.isAopProxy(orgBean)) {
            try {
                orgBean = ((Advised)orgBean).getTargetSource().getTarget();
            } catch (Exception e) {
                throw new RuntimeException("get target bean failed", e);
            }
        }
        Class<?> subType = orgBean.getClass();
        ExtensionImpl anno = subType.getDeclaredAnnotation(ExtensionImpl.class);

        // 获取注解属性值
        Class<?>[] superTypeArr = anno.extendFrom();
        String[] scenarioCodeArr = anno.applyTo();
        // 未指定属性值时的默认赋值
        if (superTypeArr.length == 0) {
            superTypeArr = subType.getInterfaces();
            if (superTypeArr.length > 1) {
                superTypeArr = Stream.of(superTypeArr).filter(t -> t.isAnnotationPresent(ExtensionPoint.class))
                    .toArray(Class[]::new);
            }
        }

        // 注册到缓存，方便后续获取
        for (Class<?> superType : superTypeArr) {
            Assert.isAssignable(superType, subType);
            ExtensionPoint superAnno = AnnotationUtils.findAnnotation(superType, ExtensionPoint.class);
            String extPointCode =
                superAnno == null || !StringUtils.hasText(superAnno.value()) ? superType.getName() : superAnno.value();
            for (String scenarioCode : scenarioCodeArr) {
                Object preVal = extImplTable.put(extPointCode, scenarioCode, bean);
                if (preVal != null) {
                    LOGGER.error("重复注册扩展点={},适用场景={}", extPointCode, scenarioCode);
                    throw new RuntimeException(String.format("重复注册扩展点=%s,适用场景=%s", extPointCode, scenarioCode));
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public <T> T locateComponent(Class<T> superType, String scenarioCode) {
        ExtensionPoint superAnno = AnnotationUtils.findAnnotation(superType, ExtensionPoint.class);
        String extPointCode =
            superAnno == null || !StringUtils.hasText(superAnno.value()) ? superType.getName() : superAnno.value();
        // 查询扩展实现
        Object bean = extImplTable.get(extPointCode, scenarioCode);
        if (bean == null) {
            // 如果根据提供的扩展场景code找不到扩展实现，则使用默认的
            bean = extImplTable.get(extPointCode, ScenarioCapable.SCENARIO_CODE_DEFAULT);
        }
        if (bean == null) {
            LOGGER.warn("根据扩展点={},适用场景={}找不到扩展实现", extPointCode, scenarioCode);
            return null;
        }
        Assert.isAssignable(superType, bean.getClass());
        return (T)bean;
    }

}
